﻿#include"Game.h"

//控制台坐标定位
void SetPos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄
	//根据句柄设置光标坐标
	COORD pos = { x,y };
	SetConsoleCursorPosition(handle, pos);
}

//绘制地图
void DrawMap()
{
	//上
	SetPos(0, 0);
	for (int i = 0; i <= 56; i+=2)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 25);
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}

}

//打印欢迎界面
void Welcome()
{
	//欢迎信息
	SetPos(40, 15);
	printf("欢迎来到贪吃蛇小游戏\n");
	SetPos(40, 20);
	system("pause");

	system("cls");
	SetPos(35, 10);
	printf("用↑↓←→控制移动方向,F3加速,F4减速");
	SetPos(35, 11);
	printf("加速得到更多分");
	SetPos(40, 20);
	system("pause");
	system("cls");
}

//打印帮助信息
void PrintHelpInfo()
{
	SetPos(65, 13);
	printf("按F3加速,F4减速");
	SetPos(65, 17);
	printf("版权@vampire-wpre");
}

//创建食物
void CreatFood(pSnake ps)
{
	//坐标是随机出现的,坐标不能在蛇身体和墙上
	int x = 0;
	int y = 0;	

	srand((unsigned int)time(NULL));//设置随机数种子
reset:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);

	pSnakeNode cur = ps->SnakeHead;
	while (cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto reset;
		}
		cur = cur->next;
	}

	pSnakeNode pfood = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (pfood == NULL)
	{
		return;
	}
	//设置坐标
	pfood->x = x;
	pfood->y = y;
	ps->Food = pfood;
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

//判断下一个位置是否为食物
int IsFood(pSnake ps, pSnakeNode next)
{
	if (ps->Food->x == next->x && ps->Food->y == next->y)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

//检测是否撞墙
void CheckKillByWall(pSnake ps)
{
	if ((ps->SnakeHead->x == 0 || ps->SnakeHead->x == 56) || (ps->SnakeHead->y == 0 || ps->SnakeHead->y == 25))
	{
		ps->status = KillByWall;
	}
}

//检测是否撞自己
void CheckKillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->SnakeHead->next;
	while (cur)
	{
		if (cur->x == ps->SnakeHead->x && cur->y == ps->SnakeHead->y)
		{
			ps->status = KillBySelf;
			return;
		}
		cur = cur->next;
	}
}

//初始化蛇
void InitSnake(pSnake ps)
{
	//创建5个初始蛇身结点
	pSnakeNode cur = NULL;
	for (int i = 0; i < 5; i ++ )
	{
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		assert(cur);
		cur->x = POS_X + 2 * i;
		cur->y = POS_Y;
		cur->next = NULL;
		if (ps->SnakeHead == NULL)
		{
			ps->SnakeHead = cur;
		}
		else
		{
			//头插
			cur->next = ps->SnakeHead;
			ps->SnakeHead = cur;
		}
	}

	//打印初始蛇
	cur = ps->SnakeHead;
	SetPos(cur->x, cur->y);
	wprintf(L"%lc", L'◆');
	cur = cur->next;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	
	//其他属性
	ps->dir = RIGHT;
	ps->Food = NULL;
	ps->FoodScore = 10;
	ps->Score = 0;
	ps->Speed = 200;
	ps->status = OK;
}

//吃食物
void Eat(pSnake ps, pSnakeNode next)
{
	next->next = ps->SnakeHead;
	ps->SnakeHead = next;
	
	//打印蛇
	pSnakeNode cur = ps->SnakeHead;
	SetPos(cur->x, cur->y);
	wprintf(L"%lc", L'◆');
	cur = cur->next;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->Score += ps->FoodScore;
	
	//free旧的食物
	free(ps->Food);
	//新建食物
	CreatFood(ps);
}

//下一个位置不是食物,正常移动
void Normal(pSnake ps, pSnakeNode next)
{
	//头插法插入next
	next->next = ps->SnakeHead;
	ps->SnakeHead = next;


	//释放尾结点
	pSnakeNode cur = ps->SnakeHead;
	while (cur->next->next != NULL)
	{
		cur = cur->next;
	}
	//尾结点位置打印成空格
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放尾结点
	free(cur->next);
	cur->next = NULL;

	//打印蛇身
	cur = ps->SnakeHead;
	SetPos(cur->x, cur->y);
	wprintf(L"%lc", L'◆');
	cur = cur->next;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
}

//蛇移动
void Move(pSnake ps)
{
	pSnakeNode next = (pSnakeNode)malloc(sizeof(SnakeNode));//方向改变时,蛇头在下一个坐标的位置,用next结点保存
	if (next == NULL)
	{
		return;
	}
	next->next = NULL;
	switch (ps->dir)
	{

	case UP:
		next->x = ps->SnakeHead->x;
		next->y = ps->SnakeHead->y - 1;
		break;
	case DOWN:
		next->x = ps->SnakeHead->x;
		next->y = ps->SnakeHead->y + 1;
		break;
	case LEFT:
		next->x = ps->SnakeHead->x - 2;
		next->y = ps->SnakeHead->y;
		break;
	case RIGHT:
		next->x = ps->SnakeHead->x + 2;
		next->y = ps->SnakeHead->y;
		break;
	}

	//下一个坐标处是否为食物:
	if (IsFood(ps, next))
	{
		//是食物就吃掉
		Eat(ps,next);
	}
	else
	{
		//不是食物就正常
		Normal(ps, next);
	}
	//检测是否撞墙
	CheckKillByWall(ps);

	//检测是否撞自己
	CheckKillBySelf(ps);
	

}

//游戏初始化
void GameStart(pSnake ps)
{
	//设置控制台属性
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	//隐藏光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台句柄

	CONSOLE_CURSOR_INFO cur_info;
	GetConsoleCursorInfo(handle, &cur_info);//获得控制台光标的信息

	cur_info.bVisible = false;
	SetConsoleCursorInfo(handle, &cur_info);//设置

	//打印欢迎信息
	Welcome();
	
	//地图绘制
	DrawMap();

	//初始化蛇
	InitSnake(ps);
	
	//创建食物
	CreatFood(ps);
}

//游戏运行逻辑
void GameRun(pSnake ps)
{

	//打印帮助信息
	PrintHelpInfo();

	
	do
	{
		//当前分数情况:
		SetPos(65, 10);
		printf("总分:%02d\n", ps->Score);
		SetPos(65, 11);
		printf("每个食物分数:%5d\n", ps->FoodScore);
		
		//按键监测↑ ↓ ← → ESC 空格 F3加速 F4减速
		if (CHECK_KEY_PRESS(VK_UP) && ps->dir != DOWN)//按上键并且当前方向不是向下
		{
			ps->dir = UP;
		}
		else if (CHECK_KEY_PRESS(VK_DOWN) && ps->dir != UP)//按下键并且当前方向不是向上
		{
			ps->dir = DOWN;
		}
		else if (CHECK_KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)//按左键并且当前方向不是向右
		{
			ps->dir = LEFT;
		}
		else if (CHECK_KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)//按右并且当前方向不是向左
		{
			ps->dir = RIGHT;
		}
		else if (CHECK_KEY_PRESS(VK_ESCAPE))//按ESC退出
		{
			ps->status = EXIT;
			break;
		}
		else if (CHECK_KEY_PRESS(VK_SPACE))//按空格暂停,再按一次继续游戏
		{
			while (1)
			{
				Sleep(100);
				if (CHECK_KEY_PRESS(VK_SPACE))
				{
					break;
				}
			}
		}
		else if (CHECK_KEY_PRESS(VK_F3))//按F3加速,休眠时间变短,食物分数变大
		{
			if (ps->Speed >= 40)//最小速度为40
			{
				ps->Speed -= 20;
				ps->FoodScore += 2;
			}
			
		}
		else if (CHECK_KEY_PRESS(VK_F4))//按F4减速,休眠时间变长,食物分数变小
		{
			if (ps->FoodScore > 4)
			{
				ps->Speed += 20;
				ps->FoodScore -= 2;
			}
		}

		//休眠一下
		Sleep(ps->Speed);

		//走一步
		Move(ps);
		
		
	} while (ps->status==OK);

	
}

//游戏善后工作
void GameEnd(pSnake ps)
{
	switch (ps->status)
	{
	case EXIT:
		SetPos(24, 12);
		printf("主动退出");
		break;
	case KillByWall:
		SetPos(24, 12);
		printf("撞墙了!");
		break;
	case KillBySelf:
		SetPos(24, 12);
		printf("撞到自己了!");
		break;
	}

	//释放malloc开辟的空间
	pSnakeNode cur = ps->SnakeHead;
	pSnakeNode del = NULL;
	while (cur)
	{
		del = cur;
		cur = cur->next;
		free(del);
	}
	free(ps->Food);
	ps->Food = NULL;
	ps = NULL;
}